package designpatterns.strategy.code;

import designpatterns.utils.ScanClassUtil;

import java.util.List;

/**
 * 策略模式
 * 意图：定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
 * 主要解决：在有多种算法相似的情况下，使用 if...else 所带来的复杂和难以维护。
 * 何时使用：一个系统有许多许多类，而区分它们的只是他们直接的行为。
 *
 * 优点： 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
 * 缺点： 1、策略类会增多。 2、所有策略类都需要对外暴露。
 * 使用场景： 1、如果在一个系统里面有许多类，它们之间的区别仅在于它们的行为，那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
 * 2、一个系统需要动态地在几种算法中选择一种。
 * 3、如果一个对象有很多的行为，如果不用恰当的模式，这些行为就只好使用多重的条件选择语句来实现。
 * 注意事项：如果一个系统的策略多于四个，就需要考虑使用混合模式，解决策略类膨胀的问题。
 */
public class Client {

    public static void main(String[] args) throws Exception {
        // 一般写法
//        noStrategy();
        // 策略模式
//        simpleStrategy();
        // 工厂模式 + 策略模式
//        strategyWithFactory();
        // 自动注册 + 工厂模式 + 策略模式
        autoRegisterStrategyWithFactory();
    }

    /**
     * 简单常规写法（if,else,switch）：
     */
    private static void noStrategy() {
        // 参数
        String mode = "+";
        double a = 1;
        double b = 2;

        // --------以下为实现逻辑------------
        switch (mode) {
            case "+":
                System.out.println(a + b);break;
            case "-":
                System.out.println(a - b);break;
            case "*":
                System.out.println(a * b);break;
            case "/":
                System.out.println(a / b);break;
        }
    }


    /**
     * 简单使用
     */
    private static void simpleStrategy() {
        // 参数
        double a = 1;
        double b = 2;
        Strategy strategy = new Add();

        // --------以下为实现逻辑------------
        Context context1 = new Context(strategy);
        Double r3 = context1.execute(a, b);
        System.out.println(r3);
    }

    /**
     * 工厂模式 + 策略模式
     */
    private static void strategyWithFactory() {
        // 参数
        double a = 1;
        double b = 2;
        String strategyName = "+";

        // --------以下为实现逻辑------------
        // 简单工厂模式逻辑
        StrategySimpleFactory simpleFactory = new StrategySimpleFactory();
        Strategy strategy = simpleFactory.getStrategy(strategyName);

        // 策略模式逻辑
        Context context = new Context(strategy);
        Double r3 = context.execute(a, b);
        System.out.println(r3);
    }

    /**
     * 最终优化: 自动注册 + 工厂模式 + 策略模式,
     * 实际开发,注册逻辑使用对象管理框架比如Spring拓展实现
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private static void autoRegisterStrategyWithFactory() throws InstantiationException, IllegalAccessException {
        // 参数
        double a = 1;
        double b = 2;
        String strategyName = "%";

        // --------以下为实现逻辑------------
        // 模拟程序启动-自动注册(Spring框架)
        autoRegisterStrategy();

        // 工厂模式逻辑
        Strategy strategy = StrategyFactory.getStrategy(strategyName);

        // 策略模式逻辑
        Context context = new Context(strategy);
        Double r3 = context.execute(a, b);
        System.out.println(r3);
    }

    /**
     * 1.扫描包：designpatterns.strategy.code下的所有.class文件
     * 2.判断class是否实现Strategy接口
     * 3.创建策略对象并调用注册方法
     *
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private static void autoRegisterStrategy() throws InstantiationException, IllegalAccessException {
        // ScanClassUtil是作者自己写的工具类,"designpatterns.strategy.code"是作者代码所在的路径。
        // 所有.class文件
        List<Class> classes = ScanClassUtil.scan("designpatterns.strategy.code");
        for (int i = 0; i < classes.size(); i++) {
            Class clazz = classes.get(i);
            // 判断是否实现Strategy接口
            boolean assignableFrom = Strategy.class.isAssignableFrom(clazz);
            if (assignableFrom && !clazz.equals(Strategy.class)) {
                // 创建对象
                Strategy strategy = (Strategy)clazz.newInstance();
                // 自动注册
                StrategyFactory.register(strategy.strategyName(),strategy);
            }
        }
    }


}
